Hirdetés

Alkalmazásfejlesztés badára: Gömb tesszellálás és animálás

Bevezető

Az előző bejegyzésekből már kiderült, hogy OpenGL-ben az objektumainkat háromszöghálóból (a szakzsargonban: “mesh”) rakjuk össze. Azonban láthattuk, hogy ez nem leányálom: egy komolyabb test nem ritka hogy több ezer háromszöget is tartalmaz, ezeket csak a mazochisták csinálják az előző bejegyzésben ismertetett módszerrel. Viszont, mint szinte mindent a mai világunkban, ezt is lehet valamilyen szinten automatizálni. Lássuk, hogyan!

Egy kis matek

Ehhez a részhez szükség lesz egy kis matematikai vénára, de nem kell félni, gyengéd leszek. A lényeg, hogy kell egy algoritmus, ami kidobja magából az egyes háromszögek pontjait, így nekünk nincs más dolgunk, mint az így kapott háromszöghalmazt kirajzoltatni az OpenGL-lel. A Déscartes-féle koordinátarendszerben nem ismerek olyan módszert, ami egy gömbfelület mentén kiválaszt nekem pontokat, de nem szabad csüggedni, van megoldás. Az egész alapja az, hogy a gömb egy forgástest, sőt, létezik egy róla elnevezett koordináta-rendszer is: az úgynevezett gömbi koordinátarendszer. Ennek segítségével lehet egy adott sugarú gömre illeszkedő ponthalmazt a legegyszerűbben vizsgálni. Három koordinátánk van, egy sugár és a két forgásszög, θ és φ (lásd ábra).

A gömbi koordinátarendszer
A gömbi koordinátarendszer

Egy pontot úgy határozhatunk meg, hogy elforgatjuk θ-val és φ-vel, így megkapjuk az origóból mutató irányát, majd ezen vektor hosszát r-re (sugár) állítjuk. Az alapötlet annyi, hogy megadjuk a tesszelláció mértékét (legyen n), a 360°-ot elosztjuk ezzel a számmal, majd x szögenként végiglépkedünk θ szerint és minden x θ-szögnél, majd a körre merőleges irányban végiglépkedünk szintén x szögenként, viszont a φ mentén. Így kapunk n*n pontot, amelyek egy gömb felületén helyezkednek el, egymástól ugyanakkora távolságban. Erre már lehetne építkezni, csak az a probléma, hogy nem homogén, hanem gömbi koordinátákban rendelkezünk felettük. Viszont három egyszerű trigonometrikus egyenlet segítségével (érdemes beincludeolni a math.h-t) átválthatjuk őket (lásd kép). Jöhet a kódolás!

Áttérés gömbi koordinátákról Descartes koordinátákra
Áttérés gömbi koordinátákról Descartes koordinátákra

Code me tender

A kódom épít egy előzőleg megírt osztályra, a ‘Vector’-ra. Ez egy nagyon egyszerű osztály, mindössze három float-ot tartalmaz, és egy vertex-et képvisel.

A gömbi koordinátarendszerben nem fok, hanem radián a forgás mértékegysége, ezért az osztályunk elején érdemes lerögzíteni a π-t, például így: #define PI 3.1415 . Az osztályomnak két tagváltozója van, egy radius (sugár) és egy level (a tesszelláció mértékét jelzi). A kötelező konstruktorokon és destruktoron kívül három függvényen keresztül tart a varázslat.

Az első a calculatePoly, ami a gömböt lefedő poligonok (azaz két háromszögből álló lapok) rajzolását végzi. Itt lövöm be a normálvektorokat is (ezekről a következő bejegyzésben bővebben), és vertex array-ként kirajzolom őket. Az egyes vertexek normálvektorai megegyeznek a vertex koordinátáival, azzal a különbséggel, hogy ezeket még normálni kell (hszen normálvektorok), azaz a hosszukat pontosan egyre állítani. Ehhez minden koordinátát le kell osztani a vektor hosszával, jelen esetben a sugárral.

A második függvény a calculateVertex, ez felel a gömbi-homogén átváltásért. Két floatot vár, ezek a θ és φ szögek, ezekből és a sugárból a fentebb látott egyenletek segítségével kicsapja a Déscartes-koordinátákat, majd a kapott vertexeket megeteti a calculatePoly-val.

A hierarchia csúcsán a drawSphere függvény áll, ez számítja ki a fent említett pontok gömbi koordinátáit. A működése egyszerű: két egymásba ágyazott for ciklussal (mindkettő 2π/levelenként lépked), és két tömbben tárolja kapott szögeket (thetastore és phistore). Ez után ugyanígy két for ciklussal végigmegyek a tömbökön, kiszedem a megfelelő koordinátákat, meghívom a fenti két függvényt. Az alábbi képen látható az eredmény drótváz, árnyalás nélküli és Goraud-árnyalásos formában.

Az osztály nagyon egyszerűen használható, deklarálás után a draw függvényben (vagy akárhol) meg kell hívni a drawSphere függvényét.

Animánia

Eljött az idő, hogy feldobjuk a szerény kis appjainkat: mozgassuk az objektumokat! Ehhez szerencsére egy nagyon jól használható eszközt adott a kezünkbe a Samsung (majdnem olyan jól használható, mintha a standard OpenGL-lel animálnánk), a Timer nevű objektumot. Egy kicsit át kell írnunk az előző bejegyzésbeli keretünket a timer használatához, az onAppInitializing metódusban létre kell hoznunk, és inicializálnunk (pTimer = new Timer és pTimer->Construct(*this)). Ezt indítjuk el az egyik új függvényben, az onForeground-ban. A működése: a timer egy olyan időzítő, amely a megadott időnként lejár. A másik új függvényünkben ezt használjuk ki: megadunk neki egy értéket millisecundumban, elindítjuk, és ha lejárt, rajzolunk egyet. Az emberi szem másodpercenként 24 képkockás sebességtől látja folyamatosnak a mozgóképet, ezt fogjuk kihasználni, 1000/24-gyel hívjuk meg a timert. Azaz másodpercenként 24-szer rajzolunk.

A mozgatáshoz bevett szokás az úgynevezett futó változó használata. Ez azt jelenti, hogy például felveszek egy float forgasszog = 0 változót, minden draw-nál megnövelem az értékét, és ennek megfelelően forgatom el az objektumomat. A letölthető kódban láthattok forgatásra és mozgatásra is példákat.

Ezzel a más koordinátarendszerre való áttérősdivel egyszerűen lehet például hengert és kúpot is alkotni, a közeljövőben ezeket is beiktatom. A következő bejegyzésben pedig az árnyalásé és a fényé (az alkoholmentes változaté, nem a simáé) lesz a főszerep.

holdmester

Azóta történt

Előzmények